package com.william.jdbcplus.core.util.druid;

import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.filter.logging.LogFilterMBean;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.proxy.jdbc.*;
import com.alibaba.druid.sql.SQLUtils.FormatOption;
import com.alibaba.druid.util.JdbcUtils;
import com.alibaba.druid.util.MySqlUtils;

import java.sql.Connection;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public abstract class LogFilter extends FilterEventAdapter implements LogFilterMBean {
    protected String dataSourceLoggerName = "druid.sql.DataSource";
    protected String connectionLoggerName = "druid.sql.Connection";
    protected String statementLoggerName = "druid.sql.Statement";
    protected String resultSetLoggerName = "druid.sql.ResultSet";
    private boolean connectionConnectBeforeLogEnable = true;
    private boolean connectionConnectAfterLogEnable = true;
    private boolean connectionCommitAfterLogEnable = true;
    private boolean connectionRollbackAfterLogEnable = true;
    private boolean connectionCloseAfterLogEnable = true;
    private boolean statementCreateAfterLogEnable = true;
    private boolean statementPrepareAfterLogEnable = true;
    private boolean statementPrepareCallAfterLogEnable = true;
    private boolean statementExecuteAfterLogEnable = true;
    private boolean statementExecuteQueryAfterLogEnable = true;
    private boolean statementExecuteUpdateAfterLogEnable = true;
    private boolean statementExecuteBatchAfterLogEnable = true;
    private boolean statementExecutableSqlLogEnable = false;
    private boolean statementCloseAfterLogEnable = true;
    private boolean statementParameterClearLogEnable = true;
    private boolean statementParameterSetLogEnable = true;
    private boolean resultSetNextAfterLogEnable = true;
    private boolean resultSetOpenAfterLogEnable = true;
    private boolean resultSetCloseAfterLogEnable = true;
    private boolean dataSourceLogEnabled = true;
    private boolean connectionLogEnabled = true;
    private boolean connectionLogErrorEnabled = true;
    private boolean statementLogEnabled = true;
    private boolean statementLogErrorEnabled = true;
    private boolean resultSetLogEnabled = true;
    private boolean resultSetLogErrorEnabled = true;
    private FormatOption statementSqlFormatOption = new FormatOption(false, true);
    private boolean statementLogSqlPrettyFormat = false;
    protected DataSourceProxy dataSource;

    public LogFilter() {
        this.configFromProperties(System.getProperties());
    }

    @Override
    public void configFromProperties(Properties properties) {
        String prop = properties.getProperty("druid.log.conn");
        if ("false".equals(prop)) {
            this.connectionLogEnabled = false;
        } else if ("true".equals(prop)) {
            this.connectionLogEnabled = true;
        }

        prop = properties.getProperty("druid.log.stmt");
        if ("false".equals(prop)) {
            this.statementLogEnabled = false;
        } else if ("true".equals(prop)) {
            this.statementLogEnabled = true;
        }

        prop = properties.getProperty("druid.log.rs");
        if ("false".equals(prop)) {
            this.resultSetLogEnabled = false;
        } else if ("true".equals(prop)) {
            this.resultSetLogEnabled = true;
        }

        prop = properties.getProperty("druid.log.stmt.executableSql");
        if ("true".equals(prop)) {
            this.statementExecutableSqlLogEnable = true;
        } else if ("false".equals(prop)) {
            this.statementExecutableSqlLogEnable = false;
        }

    }

    @Override
    public void init(DataSourceProxy dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public boolean isConnectionLogErrorEnabled() {
        return this.connectionLogErrorEnabled;
    }

    @Override
    public boolean isResultSetCloseAfterLogEnabled() {
        return this.isResultSetLogEnabled() && this.resultSetCloseAfterLogEnable;
    }

    @Override
    public void setResultSetCloseAfterLogEnabled(boolean resultSetCloseAfterLogEnable) {
        this.resultSetCloseAfterLogEnable = resultSetCloseAfterLogEnable;
    }

    @Override
    public void setConnectionLogErrorEnabled(boolean connectionLogErrorEnabled) {
        this.connectionLogErrorEnabled = connectionLogErrorEnabled;
    }

    @Override
    public boolean isResultSetLogErrorEnabled() {
        return this.resultSetLogErrorEnabled;
    }

    @Override
    public void setResultSetLogErrorEnabled(boolean resultSetLogErrorEnabled) {
        this.resultSetLogErrorEnabled = resultSetLogErrorEnabled;
    }

    @Override
    public boolean isConnectionConnectBeforeLogEnabled() {
        return this.isConnectionLogEnabled() && this.connectionConnectBeforeLogEnable;
    }

    @Override
    public void setConnectionConnectBeforeLogEnabled(boolean beforeConnectionConnectLogEnable) {
        this.connectionConnectBeforeLogEnable = beforeConnectionConnectLogEnable;
    }

    @Override
    public boolean isConnectionCloseAfterLogEnabled() {
        return this.isConnectionLogEnabled() && this.connectionCloseAfterLogEnable;
    }

    public boolean isConnectionRollbackAfterLogEnabled() {
        return this.isConnectionLogEnabled() && this.connectionRollbackAfterLogEnable;
    }

    public void setConnectionRollbackAfterLogEnabled(boolean connectionRollbackAfterLogEnable) {
        this.connectionRollbackAfterLogEnable = connectionRollbackAfterLogEnable;
    }

    @Override
    public void setConnectionCloseAfterLogEnabled(boolean afterConnectionCloseLogEnable) {
        this.connectionCloseAfterLogEnable = afterConnectionCloseLogEnable;
    }

    @Override
    public boolean isConnectionCommitAfterLogEnabled() {
        return this.isConnectionLogEnabled() && this.connectionCommitAfterLogEnable;
    }

    @Override
    public void setConnectionCommitAfterLogEnabled(boolean afterConnectionCommitLogEnable) {
        this.connectionCommitAfterLogEnable = afterConnectionCommitLogEnable;
    }

    @Override
    public boolean isConnectionConnectAfterLogEnabled() {
        return this.isConnectionLogEnabled() && this.connectionConnectAfterLogEnable;
    }

    @Override
    public void setConnectionConnectAfterLogEnabled(boolean afterConnectionConnectLogEnable) {
        this.connectionConnectAfterLogEnable = afterConnectionConnectLogEnable;
    }

    @Override
    public boolean isResultSetNextAfterLogEnabled() {
        return this.isResultSetLogEnabled() && this.resultSetNextAfterLogEnable;
    }

    @Override
    public void setResultSetNextAfterLogEnabled(boolean afterResultSetNextLogEnable) {
        this.resultSetNextAfterLogEnable = afterResultSetNextLogEnable;
    }

    @Override
    public boolean isResultSetOpenAfterLogEnabled() {
        return this.isResultSetLogEnabled() && this.resultSetOpenAfterLogEnable;
    }

    @Override
    public void setResultSetOpenAfterLogEnabled(boolean afterResultSetOpenLogEnable) {
        this.resultSetOpenAfterLogEnable = afterResultSetOpenLogEnable;
    }

    @Override
    public boolean isStatementCloseAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementCloseAfterLogEnable;
    }

    @Override
    public void setStatementCloseAfterLogEnabled(boolean afterStatementCloseLogEnable) {
        this.statementCloseAfterLogEnable = afterStatementCloseLogEnable;
    }

    @Override
    public boolean isStatementCreateAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementCreateAfterLogEnable;
    }

    @Override
    public void setStatementCreateAfterLogEnabled(boolean afterStatementCreateLogEnable) {
        this.statementCreateAfterLogEnable = afterStatementCreateLogEnable;
    }

    @Override
    public boolean isStatementExecuteBatchAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementExecuteBatchAfterLogEnable;
    }

    @Override
    public void setStatementExecuteBatchAfterLogEnabled(boolean afterStatementExecuteBatchLogEnable) {
        this.statementExecuteBatchAfterLogEnable = afterStatementExecuteBatchLogEnable;
    }

    @Override
    public boolean isStatementExecuteAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementExecuteAfterLogEnable;
    }

    @Override
    public void setStatementExecuteAfterLogEnabled(boolean afterStatementExecuteLogEnable) {
        this.statementExecuteAfterLogEnable = afterStatementExecuteLogEnable;
    }

    @Override
    public boolean isStatementExecuteQueryAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementExecuteQueryAfterLogEnable;
    }

    @Override
    public void setStatementExecuteQueryAfterLogEnabled(boolean afterStatementExecuteQueryLogEnable) {
        this.statementExecuteQueryAfterLogEnable = afterStatementExecuteQueryLogEnable;
    }

    @Override
    public boolean isStatementExecuteUpdateAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementExecuteUpdateAfterLogEnable;
    }

    @Override
    public void setStatementExecuteUpdateAfterLogEnabled(boolean afterStatementExecuteUpdateLogEnable) {
        this.statementExecuteUpdateAfterLogEnable = afterStatementExecuteUpdateLogEnable;
    }

    public boolean isStatementExecutableSqlLogEnable() {
        return this.statementExecutableSqlLogEnable;
    }

    public void setStatementExecutableSqlLogEnable(boolean statementExecutableSqlLogEnable) {
        this.statementExecutableSqlLogEnable = statementExecutableSqlLogEnable;
    }

    @Override
    public boolean isStatementPrepareCallAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementPrepareCallAfterLogEnable;
    }

    @Override
    public void setStatementPrepareCallAfterLogEnabled(boolean afterStatementPrepareCallLogEnable) {
        this.statementPrepareCallAfterLogEnable = afterStatementPrepareCallLogEnable;
    }

    @Override
    public boolean isStatementPrepareAfterLogEnabled() {
        return this.isStatementLogEnabled() && this.statementPrepareAfterLogEnable;
    }

    @Override
    public void setStatementPrepareAfterLogEnabled(boolean afterStatementPrepareLogEnable) {
        this.statementPrepareAfterLogEnable = afterStatementPrepareLogEnable;
    }

    @Override
    public boolean isDataSourceLogEnabled() {
        return this.dataSourceLogEnabled;
    }

    @Override
    public void setDataSourceLogEnabled(boolean dataSourceLogEnabled) {
        this.dataSourceLogEnabled = dataSourceLogEnabled;
    }

    @Override
    public boolean isConnectionLogEnabled() {
        return this.connectionLogEnabled;
    }

    @Override
    public void setConnectionLogEnabled(boolean connectionLogEnabled) {
        this.connectionLogEnabled = connectionLogEnabled;
    }

    @Override
    public boolean isStatementLogEnabled() {
        return this.statementLogEnabled;
    }

    @Override
    public void setStatementLogEnabled(boolean statementLogEnabled) {
        this.statementLogEnabled = statementLogEnabled;
    }

    @Override
    public boolean isStatementLogErrorEnabled() {
        return this.statementLogErrorEnabled;
    }

    @Override
    public void setStatementLogErrorEnabled(boolean statementLogErrorEnabled) {
        this.statementLogErrorEnabled = statementLogErrorEnabled;
    }

    @Override
    public boolean isResultSetLogEnabled() {
        return this.resultSetLogEnabled;
    }

    @Override
    public void setResultSetLogEnabled(boolean resultSetLogEnabled) {
        this.resultSetLogEnabled = resultSetLogEnabled;
    }

    @Override
    public boolean isStatementParameterSetLogEnabled() {
        return this.isStatementLogEnabled() && this.statementParameterSetLogEnable;
    }

    @Override
    public void setStatementParameterSetLogEnabled(boolean statementParameterSetLogEnable) {
        this.statementParameterSetLogEnable = statementParameterSetLogEnable;
    }

    public boolean isStatementParameterClearLogEnable() {
        return this.isStatementLogEnabled() && this.statementParameterClearLogEnable;
    }

    public void setStatementParameterClearLogEnable(boolean statementParameterClearLogEnable) {
        this.statementParameterClearLogEnable = statementParameterClearLogEnable;
    }

    @Override
    public FormatOption getStatementSqlFormatOption() {
        return this.statementSqlFormatOption;
    }

    @Override
    public void setStatementSqlFormatOption(FormatOption formatOption) {
        this.statementSqlFormatOption = formatOption;
    }

    @Override
    public boolean isStatementSqlPrettyFormat() {
        return this.statementLogSqlPrettyFormat;
    }

    @Override
    public void setStatementSqlPrettyFormat(boolean statementSqlPrettyFormat) {
        this.statementLogSqlPrettyFormat = statementSqlPrettyFormat;
    }

    protected abstract void connectionLog(String var1);

    protected abstract void statementLog(String var1);

    protected abstract void statementLogError(String var1, Throwable var2);

    protected abstract void resultSetLog(String var1);

    protected abstract void resultSetLogError(String var1, Throwable var2);

    @Override
    public void connection_connectAfter(ConnectionProxy connection) {
        if (connection != null) {
            if (this.connectionConnectAfterLogEnable && this.isConnectionLogEnabled()) {
                StringBuilder msg = (new StringBuilder(34)).append("{conn-").append(connection.getId());
                Connection impl = connection.getRawObject();
                if ("mysql".equals(this.dataSource.getDbType())) {
                    Long procId = MySqlUtils.getId(impl);
                    if (procId != null) {
                        msg.append(",procId-").append(procId);
                    }
                }

                msg.append("} connected");
                this.connectionLog(msg.toString());
            }

        }
    }

    @Override
    public Savepoint connection_setSavepoint(FilterChain chain, ConnectionProxy connection) throws SQLException {
        Savepoint savepoint = chain.connection_setSavepoint(connection);
        if (this.isConnectionLogEnabled()) {
            this.connectionLog("{conn " + connection.getId() + "} setSavepoint-" + this.savepointToString(savepoint));
        }

        return savepoint;
    }

    @Override
    public Savepoint connection_setSavepoint(FilterChain chain, ConnectionProxy connection, String name) throws SQLException {
        Savepoint savepoint = chain.connection_setSavepoint(connection, name);
        if (this.isConnectionLogEnabled()) {
            this.connectionLog("{conn " + connection.getId() + "} setSavepoint-" + name);
        }

        return savepoint;
    }

    @Override
    public void connection_rollback(FilterChain chain, ConnectionProxy connection) throws SQLException {
        super.connection_rollback(chain, connection);
        if (this.connectionRollbackAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn " + connection.getId() + "} rollback");
        }
    }

    @Override
    public void connection_rollback(FilterChain chain, ConnectionProxy connection, Savepoint savePoint) throws SQLException {
        super.connection_rollback(chain, connection, savePoint);
        if (this.connectionRollbackAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn " + connection.getId() + "} rollback -> " + this.savepointToString(savePoint));
        }

    }

    @Override
    public void connection_commit(FilterChain chain, ConnectionProxy connection) throws SQLException {
        super.connection_commit(chain, connection);
        if (this.connectionCommitAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn-" + connection.getId() + "} commited");
        }

    }

    @Override
    public void connection_setAutoCommit(FilterChain chain, ConnectionProxy connection, boolean autoCommit) throws SQLException {
        this.connectionLog("{conn-" + connection.getId() + "} setAutoCommit " + autoCommit);
        chain.connection_setAutoCommit(connection, autoCommit);
    }

    @Override
    public void connection_close(FilterChain chain, ConnectionProxy connection) throws SQLException {
        super.connection_close(chain, connection);
        if (this.connectionCloseAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn-" + connection.getId() + "} closed");
        }
    }

    @Override
    public void statement_close(FilterChain chain, StatementProxy statement) throws SQLException {
        super.statement_close(chain, statement);
        if (this.statementCloseAfterLogEnable && this.isStatementLogEnabled()) {
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} closed");
        }
    }

    @Override
    protected void statementExecuteBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
        if (statement instanceof PreparedStatementProxy) {
            this.logParameter((PreparedStatementProxy) statement);
        }
    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {
        this.logExecutableSql(statement, sql);
        if (this.statementExecuteAfterLogEnable && this.isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = (double) statement.getLastExecuteTimeNano();
            double millis = nanos / 1000000.0D;
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} executed. " + millis + " millis. \n" + sql);
        }
    }

    @Override
    protected void statementExecuteBatchBefore(StatementProxy statement) {
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
        String sql;
        if (statement instanceof PreparedStatementProxy) {
            sql = ((PreparedStatementProxy) statement).getSql();
        } else {
            sql = statement.getBatchSql();
        }

        this.logExecutableSql(statement, sql);
        if (this.statementExecuteBatchAfterLogEnable && this.isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = (double) statement.getLastExecuteTimeNano();
            double millis = nanos / 1000000.0D;
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} batch executed. " + millis + " millis. \n" + sql);
        }
    }

    @Override
    protected void statementExecuteQueryBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
        if (statement instanceof PreparedStatementProxy) {
            this.logParameter((PreparedStatementProxy) statement);
        }
    }

    @Override
    protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
        this.logExecutableSql(statement, sql);
        if (this.statementExecuteQueryAfterLogEnable && this.isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = (double) statement.getLastExecuteTimeNano();
            double millis = nanos / 1000000.0D;
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + ", rs-" + resultSet.getId() + "} query executed. " + millis + " millis. \n" + sql);
        }
    }

    @Override
    protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
        if (statement instanceof PreparedStatementProxy) {
            this.logParameter((PreparedStatementProxy) statement);
        }
    }

    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        this.logExecutableSql(statement, sql);
        if (this.statementExecuteUpdateAfterLogEnable && this.isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = (double) statement.getLastExecuteTimeNano();
            double millis = nanos / 1000000.0D;
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} update executed. effort " + updateCount + ". " + millis + " millis. \n" + sql);
        }
    }

    private void logExecutableSql(StatementProxy statement, String sql) {
        if (!isStatementExecutableSqlLogEnable()) {
            return;
        }

        statement.setLastExecuteTimeNano();
        double nanos = statement.getLastExecuteTimeNano();
        double millis = nanos / (1000 * 1000);

        int parametersSize = statement.getParametersSize();
        if (parametersSize == 0) {
            if (!sql.equalsIgnoreCase("select 1")) {
                statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed consume " + millis
                        + " msec.  \n" + sql);
            }
            return;
        }

        List<Object> parameters = new ArrayList<Object>(parametersSize);
        for (int i = 0; i < parametersSize; ++i) {
            JdbcParameter jdbcParam = statement.getParameter(i);
            parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
        }

        String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
        String formattedSql = SQLUtils.format(sql, dbType, parameters, this.statementSqlFormatOption);
        statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed consume " + millis + " msec. \n"
                + formattedSql);
    }

    @Override
    public void resultSet_close(FilterChain chain, ResultSetProxy resultSet) throws SQLException {
        chain.resultSet_close(resultSet);
        StringBuffer buf = new StringBuffer();
        buf.append("{conn-");
        buf.append(resultSet.getStatementProxy().getConnectionProxy().getId());
        buf.append(", ");
        buf.append(this.stmtId(resultSet));
        buf.append(", rs-");
        buf.append(resultSet.getId());
        buf.append("} closed");
        if (this.isResultSetCloseAfterLogEnabled()) {
            this.resultSetLog(buf.toString());
        }
    }

    @Override
    public boolean resultSet_next(FilterChain chain, ResultSetProxy resultSet) throws SQLException {
        boolean moreRows = super.resultSet_next(chain, resultSet);
        if (moreRows && this.resultSetNextAfterLogEnable && this.isResultSetLogEnabled()) {
            try {
                StringBuffer buf = new StringBuffer();
                buf.append("{conn-");
                buf.append(resultSet.getStatementProxy().getConnectionProxy().getId());
                buf.append(", ");
                buf.append(this.stmtId(resultSet));
                buf.append(", rs-");
                buf.append(resultSet.getId());
                buf.append("}");
                buf.append(" Result: [");
                ResultSetMetaData meta = resultSet.getMetaData();
                int i = 0;

                for (int size = meta.getColumnCount(); i < size; ++i) {
                    if (i != 0) {
                        buf.append(", ");
                    }

                    int columnIndex = i + 1;
                    int type = meta.getColumnType(columnIndex);
                    Object value;
                    if (type == 93) {
                        value = resultSet.getTimestamp(columnIndex);
                    } else if (type == 2004) {
                        value = "<BLOB>";
                    } else if (type == 2005) {
                        value = "<CLOB>";
                    } else if (type == 2011) {
                        value = "<NCLOB>";
                    } else if (type == -2) {
                        value = "<BINARY>";
                    } else {
                        value = resultSet.getObject(columnIndex);
                    }

                    buf.append(value);
                }

                buf.append("]");
                this.resultSetLog(buf.toString());
            } catch (SQLException var11) {
                this.resultSetLogError("logging error", var11);
            }
        }

        return moreRows;
    }

    @Override
    public Object callableStatement_getObject(FilterChain chain, CallableStatementProxy statement, int parameterIndex) throws SQLException {
        Object obj = chain.callableStatement_getObject(statement, parameterIndex);
        if (obj instanceof ResultSetProxy) {
            this.resultSetOpenAfter((ResultSetProxy) obj);
        }

        return obj;
    }

    @Override
    public Object callableStatement_getObject(FilterChain chain, CallableStatementProxy statement, int parameterIndex, Map<String, Class<?>> map) throws SQLException {
        Object obj = chain.callableStatement_getObject(statement, parameterIndex, map);
        if (obj instanceof ResultSetProxy) {
            this.resultSetOpenAfter((ResultSetProxy) obj);
        }

        return obj;
    }

    @Override
    public Object callableStatement_getObject(FilterChain chain, CallableStatementProxy statement, String parameterName) throws SQLException {
        Object obj = chain.callableStatement_getObject(statement, parameterName);
        if (obj instanceof ResultSetProxy) {
            this.resultSetOpenAfter((ResultSetProxy) obj);
        }

        return obj;
    }

    @Override
    public Object callableStatement_getObject(FilterChain chain, CallableStatementProxy statement, String parameterName, Map<String, Class<?>> map) throws SQLException {
        Object obj = chain.callableStatement_getObject(statement, parameterName, map);
        if (obj instanceof ResultSetProxy) {
            this.resultSetOpenAfter((ResultSetProxy) obj);
        }

        return obj;
    }

    @Override
    protected void resultSetOpenAfter(ResultSetProxy resultSet) {
        if (this.resultSetOpenAfterLogEnable && this.isResultSetLogEnabled()) {
            try {
                StringBuffer buf = new StringBuffer();
                buf.append("{conn-");
                buf.append(resultSet.getStatementProxy().getConnectionProxy().getId());
                buf.append(", ");
                buf.append(this.stmtId(resultSet));
                buf.append(", rs-");
                buf.append(resultSet.getId());
                buf.append("}");
                String resultId = buf.toString();
                this.resultSetLog(resultId + " open");
                buf.append(" Header: [");
                ResultSetMetaData meta = resultSet.getMetaData();
                int i = 0;

                for (int size = meta.getColumnCount(); i < size; ++i) {
                    if (i != 0) {
                        buf.append(", ");
                    }

                    buf.append(meta.getColumnName(i + 1));
                }

                buf.append("]");
                this.resultSetLog(buf.toString());
            } catch (SQLException var7) {
                this.resultSetLogError("logging error", var7);
            }
        }

    }

    @Override
    protected void statementCreateAfter(StatementProxy statement) {
        if (this.statementCreateAfterLogEnable && this.isStatementLogEnabled()) {
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", stmt-" + statement.getId() + "} created");
        }
    }

    @Override
    protected void statementPrepareAfter(PreparedStatementProxy statement) {
        if (this.statementPrepareAfterLogEnable && this.isStatementLogEnabled()) {
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", pstmt-" + statement.getId() + "} created. \n" + statement.getSql());
        }
    }

    @Override
    protected void statementPrepareCallAfter(CallableStatementProxy statement) {
        if (this.statementPrepareCallAfterLogEnable && this.isStatementLogEnabled()) {
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", cstmt-" + statement.getId() + "} created. \n" + statement.getSql());
        }

    }

    @Override
    protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {
        if (this.isStatementLogErrorEnabled()) {
            if (!this.isStatementExecutableSqlLogEnable()) {
                this.statementLogError("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} execute error. \n" + sql, error);
            } else {
                int parametersSize = statement.getParametersSize();
                if (parametersSize > 0) {
                    List<Object> parameters = new ArrayList(parametersSize);

                    for (int i = 0; i < parametersSize; ++i) {
                        JdbcParameter jdbcParam = statement.getParameter(i);
                        parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
                    }

                    String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
                    String formattedSql = SQLUtils.format(sql, dbType, parameters, this.statementSqlFormatOption);
                    this.statementLogError("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} execute error. \n" + formattedSql, error);
                } else {
                    this.statementLogError("{conn-" + statement.getConnectionProxy().getId() + ", " + this.stmtId(statement) + "} execute error. \n" + sql, error);
                }
            }
        }
    }

    private String stmtId(ResultSetProxy resultSet) {
        return this.stmtId(resultSet.getStatementProxy());
    }

    private String stmtId(StatementProxy statement) {
        StringBuffer buf = new StringBuffer();
        if (statement instanceof CallableStatementProxy) {
            buf.append("cstmt-");
        } else if (statement instanceof PreparedStatementProxy) {
            buf.append("pstmt-");
        } else {
            buf.append("stmt-");
        }

        buf.append(statement.getId());
        return buf.toString();
    }

    protected void logParameter(PreparedStatementProxy statement) {
        if (this.isStatementParameterSetLogEnabled()) {
            StringBuffer buf = new StringBuffer();
            buf.append("{conn-");
            buf.append(statement.getConnectionProxy().getId());
            buf.append(", ");
            buf.append(this.stmtId((StatementProxy) statement));
            buf.append("}");
            buf.append(" Parameters : [");
            int i = 0;

            int parametersSize;
            JdbcParameter parameter;
            int sqlType;
            for (parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
                parameter = statement.getParameter(i);
                if (i != 0) {
                    buf.append(", ");
                }

                if (parameter != null) {
                    sqlType = parameter.getSqlType();
                    Object value = parameter.getValue();
                    switch (sqlType) {
                        case 0:
                            buf.append("NULL");
                            break;
                        default:
                            buf.append(String.valueOf(value));
                    }
                }
            }

            buf.append("]");
            this.statementLog(buf.toString());
            buf = new StringBuffer();
            buf.append("{conn-");
            buf.append(statement.getConnectionProxy().getId());
            buf.append(", ");
            buf.append(this.stmtId((StatementProxy) statement));
            buf.append("}");
            buf.append(" Types : [");
            i = 0;

            for (parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
                parameter = statement.getParameter(i);
                if (i != 0) {
                    buf.append(", ");
                }

                if (parameter != null) {
                    sqlType = parameter.getSqlType();
                    buf.append(JdbcUtils.getTypeName(sqlType));
                }
            }

            buf.append("]");
            this.statementLog(buf.toString());
        }
    }

    @Override
    public void dataSource_releaseConnection(FilterChain chain, DruidPooledConnection conn) throws SQLException {
        long connectionId = -1L;
        if (conn.getConnectionHolder() != null) {
            ConnectionProxy connection = (ConnectionProxy) conn.getConnectionHolder().getConnection();
            connectionId = connection.getId();
        }

        chain.dataSource_recycle(conn);
        if (this.connectionCloseAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn-" + connectionId + "} pool-recycle");
        }
    }

    @Override
    public DruidPooledConnection dataSource_getConnection(FilterChain chain, DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
        DruidPooledConnection conn = chain.dataSource_connect(dataSource, maxWaitMillis);
        ConnectionProxy connection = (ConnectionProxy) conn.getConnectionHolder().getConnection();
        if (this.connectionConnectAfterLogEnable && this.isConnectionLogEnabled()) {
            this.connectionLog("{conn-" + connection.getId() + "} pool-connect");
        }

        return conn;
    }

    @Override
    public void preparedStatement_clearParameters(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
        if (this.isStatementParameterClearLogEnable()) {
            this.statementLog("{conn-" + statement.getConnectionProxy().getId() + ", pstmt-" + statement.getId() + "} clearParameters. ");
        }

        chain.preparedStatement_clearParameters(statement);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) {
        return iface == this.getClass() || iface == com.alibaba.druid.filter.logging.LogFilter.class;
    }

    @Override
    public <T> T unwrap(Class<T> iface) {
        return iface != this.getClass() && iface != com.alibaba.druid.filter.logging.LogFilter.class ? null : (T) this;
    }

    protected String savepointToString(Savepoint savePoint) {
        String savePointString = null;

        try {
            savePointString = savePoint.getSavepointName();
        } catch (SQLException var6) {
            try {
                savePointString = String.valueOf(savePoint.getSavepointId());
            } catch (SQLException var5) {
                savePointString = savePoint.toString();
            }
        }

        return savePointString;
    }
}
